
package main;

import (
       //"fmt"
       "net"
       "time"
)

const (
      FAILURE_LISTENER_PORT = 25000;
      FAILURE_SENDER_PORT = 26000;

      SEND_GRANULARITY = 2e9;
      LISTEN_GRANULARITY = 1e9;
      LISTEN_TIMEOUT = 4e9;
)

// Maps IP to its current status
var FD_status = make(map[string] int);

type status_update struct {
	IP string;
	status string;
}

func udpListener(udp_sender, udp_listener *net.UDPConn, src_ip string) {
     for ;; {
     	 time.Sleep(LISTEN_GRANULARITY);
      	 //fmt.Printf("in receiver\n");
     	 var b [100]byte;

	 udp_listener.SetReadTimeout(LISTEN_TIMEOUT);
     	 n_r, addr, err_r := udp_listener.ReadFromUDP(&b);
	 n_r = n_r;
	 addr = addr;
	 if (err_r != nil) {
	    //fmt.Printf("Error in listener : %s", err_r);

	    if (FD_status[src_ip] == 1) {
	       // Was up. Now down.
	       FD_status[src_ip] = 0;

	       newstatus := new(status_update);
	       newstatus.IP = src_ip;
	       newstatus.status = "down";
	       failure_data_chan <- *newstatus;
	    } else if (FD_status[src_ip] == 0) {
	       // Do nothing.
	    }
	    continue;
	 } else {
	    //fmt.Printf("Read %d bytes from %v\n", n_r, addr);
	    if (FD_status[src_ip] == 0) {
	       // Was down. Now up.
	       FD_status[src_ip] = 1;

	       newstatus := new(status_update);
	       newstatus.IP = src_ip;
	       newstatus.status = "up";
	       failure_data_chan <- *newstatus;	    
	    } else if (FD_status[src_ip] == 1) {
	       // Do nothing.
	    }
	 }
     }
}

func udpSender(udp_sender, udp_listener *net.UDPConn, dst_ip string, dst_port int) {
     for ;; {
	 time.Sleep(SEND_GRANULARITY);
     	 //fmt.Printf("in Sender\n");
     	 var b [100]byte;
	 for i := 0; i < 10; i++ {
	     b[i] = 'a';
	 }

	 var addr net.UDPAddr;
	 addr.IP = net.ParseIP(dst_ip);
	 addr.Port = dst_port;

	 n_w, err_w := udp_sender.WriteToUDP(b[0:10], &addr);
	 n_w = n_w;
	 if (err_w != nil) {
	    //fmt.Printf("Error in sender : %s", err_w);
	    continue;
	 } else {
    	    //fmt.Printf("Written %d bytes to %v\n", n_w, addr);
	 }
     }
}

func createListener(my_ip string, port int) (*net.UDPConn) {
     var addr net.UDPAddr;
     addr.IP = net.ParseIP(my_ip);
     addr.Port = port;
     
     udp_conn, err :=  net.ListenUDP("udp", &addr);
     if (err != nil) {
     	//fmt.Printf("Error in UDP listener : %s", err);
	return nil;
     } else {
        //fmt.Printf("Successfully created listener\n");
        return udp_conn;
     }
     return nil;
}

func createSender(src_ip, dst_ip string, src_port, dst_port int) (*net.UDPConn) {
     var dst_addr net.UDPAddr;
     dst_addr.IP = net.ParseIP(dst_ip);
     dst_addr.Port = dst_port;
     
     var src_addr net.UDPAddr;
     src_addr.IP = net.ParseIP(src_ip);
     src_addr.Port = src_port;

     udp_conn, err :=  net.DialUDP("udp", &src_addr, &dst_addr);
     if (err != nil) {
     	//fmt.Printf("Error in UDP Dialer : %s", err);
	return nil;
     } else {
        //fmt.Printf("Successfully created sender\n");
       	return udp_conn;
     }
     return nil;
}

func failureDetector() {
     for i := 0; i < n_servers-1; i++ {
     	 FD_status[peer_servers[i]] = 0;
     }

     for i := 0; i < n_servers - 1; i++ {
         udp_sender := createSender(my_ip, peer_servers[i], FAILURE_SENDER_PORT, FAILURE_LISTENER_PORT + serverID);
	 udp_listener := createListener(my_ip, FAILURE_LISTENER_PORT + ip_serverid_map[peer_servers[i]]);	 

	 go udpSender(udp_sender, udp_listener, peer_servers[i], FAILURE_LISTENER_PORT + serverID);
    	 go udpListener(udp_sender, udp_listener, peer_servers[i]);
     }
     return;
}
